#include <clutter/clutter.h>
#include <math.h>
-static void clutter_container_iface_init (ClutterContainerIface *iface);
-
-static ClutterContainerIface *st_scroll_view_parent_iface = NULL;
-
+typedef struct _StScrollViewPrivate StScrollViewPrivate;
struct _StScrollViewPrivate
{
- /* a pointer to the child; this is actually stored
- * inside StBin:child, but we keep it to avoid
- * calling st_bin_get_child() every time we need it
- */
ClutterActor *child;
StAdjustment *hadjustment;
guint vscrollbar_visible : 1;
};
-G_DEFINE_TYPE_WITH_CODE (StScrollView, st_scroll_view, ST_TYPE_BIN,
- G_ADD_PRIVATE (StScrollView)
- G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
- clutter_container_iface_init))
+G_DEFINE_TYPE_WITH_PRIVATE (StScrollView, st_scroll_view, ST_TYPE_WIDGET)
enum {
PROP_0,
+ PROP_CHILD,
PROP_HSCROLL,
PROP_VSCROLL,
PROP_HSCROLLBAR_POLICY,
GValue *value,
GParamSpec *pspec)
{
- StScrollViewPrivate *priv = ((StScrollView *) object)->priv;
+ StScrollViewPrivate *priv =
+ st_scroll_view_get_instance_private (ST_SCROLL_VIEW (object));
switch (property_id)
{
+ case PROP_CHILD:
+ g_value_set_object (value, priv->child);
+ break;
case PROP_HSCROLL:
g_value_set_object (value, priv->hscroll);
break;
GParamSpec *pspec)
{
StScrollView *self = ST_SCROLL_VIEW (object);
- StScrollViewPrivate *priv = self->priv;
+ StScrollViewPrivate *priv = st_scroll_view_get_instance_private (self);
switch (property_id)
{
+ case PROP_CHILD:
+ st_scroll_view_set_child (self, g_value_get_object (value));
+ break;
case PROP_MOUSE_SCROLL:
st_scroll_view_set_mouse_scrolling (self,
g_value_get_boolean (value));
static void
st_scroll_view_dispose (GObject *object)
{
- StScrollViewPrivate *priv = ST_SCROLL_VIEW (object)->priv;
+ StScrollViewPrivate *priv =
+ st_scroll_view_get_instance_private (ST_SCROLL_VIEW (object));
/* The fade effect disconnects from the adjustments and
* needs to be removed while those are still alive.
*/
clutter_actor_clear_effects (CLUTTER_ACTOR (object));
- g_clear_pointer (&priv->vscroll, clutter_actor_destroy);
- g_clear_pointer (&priv->hscroll, clutter_actor_destroy);
+ g_clear_weak_pointer (&priv->child);
+ g_clear_weak_pointer (&priv->vscroll);
+ g_clear_weak_pointer (&priv->hscroll);
/* For most reliable freeing of memory, an object with signals
* like StAdjustment should be explicitly disposed. Since we own
* the signal handlers that we established on creation.
*/
if (priv->hadjustment)
- {
- g_object_run_dispose (G_OBJECT (priv->hadjustment));
- g_object_unref (priv->hadjustment);
- priv->hadjustment = NULL;
- }
+ g_object_run_dispose (G_OBJECT (priv->hadjustment));
+
+ g_clear_object (&priv->hadjustment);
if (priv->vadjustment)
- {
- g_object_run_dispose (G_OBJECT (priv->vadjustment));
- g_object_unref (priv->vadjustment);
- priv->vadjustment = NULL;
- }
+ g_object_run_dispose (G_OBJECT (priv->vadjustment));
+
+ g_clear_object (&priv->vadjustment);
G_OBJECT_CLASS (st_scroll_view_parent_class)->dispose (object);
}
st_scroll_view_paint (ClutterActor *actor,
ClutterPaintContext *paint_context)
{
- StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
+ StScrollViewPrivate *priv =
+ st_scroll_view_get_instance_private (ST_SCROLL_VIEW (actor));
st_widget_paint_background (ST_WIDGET (actor), paint_context);
st_scroll_view_pick (ClutterActor *actor,
ClutterPickContext *pick_context)
{
- StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
+ StScrollViewPrivate *priv =
+ st_scroll_view_get_instance_private (ST_SCROLL_VIEW (actor));
/* Chain up so we get a bounding box pained (if we are reactive) */
CLUTTER_ACTOR_CLASS (st_scroll_view_parent_class)->pick (actor, pick_context);
get_scrollbar_width (StScrollView *scroll,
gfloat for_height)
{
- StScrollViewPrivate *priv = scroll->priv;
+ StScrollViewPrivate *priv = st_scroll_view_get_instance_private (scroll);
if (clutter_actor_is_visible (priv->vscroll))
{
get_scrollbar_height (StScrollView *scroll,
gfloat for_width)
{
- StScrollViewPrivate *priv = scroll->priv;
+ StScrollViewPrivate *priv = st_scroll_view_get_instance_private (scroll);
if (clutter_actor_is_visible (priv->hscroll))
{
gfloat *min_width_p,
gfloat *natural_width_p)
{
- StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
+ StScrollViewPrivate *priv =
+ st_scroll_view_get_instance_private (ST_SCROLL_VIEW (actor));
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
gboolean account_for_vscrollbar = FALSE;
gfloat min_width = 0, natural_width;
gfloat *min_height_p,
gfloat *natural_height_p)
{
- StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
+ StScrollViewPrivate *priv =
+ st_scroll_view_get_instance_private (ST_SCROLL_VIEW (actor));
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
gboolean account_for_hscrollbar = FALSE;
gfloat min_height = 0, natural_height;
st_scroll_view_allocate (ClutterActor *actor,
const ClutterActorBox *box)
{
+ StScrollViewPrivate *priv =
+ st_scroll_view_get_instance_private (ST_SCROLL_VIEW (actor));
ClutterActorBox content_box, child_box;
gfloat avail_width, avail_height, sb_width, sb_height;
gboolean hscrollbar_visible, vscrollbar_visible;
-
- StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
clutter_actor_set_allocation (actor, box);
st_scroll_view_scroll_event (ClutterActor *self,
ClutterEvent *event)
{
- StScrollViewPrivate *priv = ST_SCROLL_VIEW (self)->priv;
+ StScrollViewPrivate *priv =
+ st_scroll_view_get_instance_private (ST_SCROLL_VIEW (self));
ClutterTextDirection direction;
ClutterScrollDirection scroll_direction;
return TRUE;
}
+static void
+st_scroll_view_popup_menu (StWidget *widget)
+{
+ StScrollViewPrivate *priv =
+ st_scroll_view_get_instance_private (ST_SCROLL_VIEW (widget));
+
+ if (priv->child && ST_IS_WIDGET (priv->child))
+ st_widget_popup_menu (ST_WIDGET (priv->child));
+}
+
+static gboolean
+st_scroll_view_navigate_focus (StWidget *widget,
+ ClutterActor *from,
+ StDirectionType direction)
+{
+ StScrollViewPrivate *priv =
+ st_scroll_view_get_instance_private (ST_SCROLL_VIEW (widget));
+ ClutterActor *bin_actor = CLUTTER_ACTOR (widget);
+
+ if (st_widget_get_can_focus (widget))
+ {
+ if (from && clutter_actor_contains (bin_actor, from))
+ return FALSE;
+
+ if (clutter_actor_is_mapped (bin_actor))
+ {
+ clutter_actor_grab_key_focus (bin_actor);
+ return TRUE;
+ }
+ else
+ return FALSE;
+ }
+ else if (priv->child && ST_IS_WIDGET (priv->child))
+ return st_widget_navigate_focus (ST_WIDGET (priv->child), from, direction, FALSE);
+ else
+ return FALSE;
+}
+
static void
st_scroll_view_class_init (StScrollViewClass *klass)
{
actor_class->scroll_event = st_scroll_view_scroll_event;
widget_class->style_changed = st_scroll_view_style_changed;
+ widget_class->popup_menu = st_scroll_view_popup_menu;
+ widget_class->navigate_focus = st_scroll_view_navigate_focus;
+
+ /**
+ * StScrollView:child:
+ *
+ * The child #StScrollable of the #StScrollView container.
+ */
+ props[PROP_CHILD] =
+ g_param_spec_object ("child", NULL, NULL,
+ ST_TYPE_SCROLLABLE,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
/**
* StScrollView:hscroll:
}
static void
-st_scroll_view_init (StScrollView *self)
+set_child (StScrollView *scroll, ClutterActor *child)
{
- StScrollViewPrivate *priv = self->priv = st_scroll_view_get_instance_private (self);
+ StScrollViewPrivate *priv = st_scroll_view_get_instance_private (scroll);
+ ClutterActor *old = priv->child;
- priv->hscrollbar_policy = ST_POLICY_AUTOMATIC;
- priv->vscrollbar_policy = ST_POLICY_AUTOMATIC;
+ if (!g_set_weak_pointer (&priv->child, child))
+ return;
- priv->hadjustment = g_object_new (ST_TYPE_ADJUSTMENT,
- "actor", self,
- NULL);
- priv->hscroll = g_object_new (ST_TYPE_SCROLL_BAR,
- "adjustment", priv->hadjustment,
- "vertical", FALSE,
- NULL);
+ if (old)
+ st_scrollable_set_adjustments (ST_SCROLLABLE (old), NULL, NULL);
- priv->vadjustment = g_object_new (ST_TYPE_ADJUSTMENT,
- "actor", self,
- NULL);
- priv->vscroll = g_object_new (ST_TYPE_SCROLL_BAR,
- "adjustment", priv->vadjustment,
- "vertical", TRUE,
- NULL);
-
- clutter_actor_add_child (CLUTTER_ACTOR (self), priv->hscroll);
- clutter_actor_add_child (CLUTTER_ACTOR (self), priv->vscroll);
+ if (priv->child)
+ st_scrollable_set_adjustments (ST_SCROLLABLE (priv->child),
+ priv->hadjustment,
+ priv->vadjustment);
- /* mouse scroll is enabled by default, so we also need to be reactive */
- priv->mouse_scroll = TRUE;
- g_object_set (G_OBJECT (self), "reactive", TRUE, NULL);
+ g_object_notify_by_pspec (G_OBJECT (scroll), props[PROP_CHILD]);
}
static void
-st_scroll_view_add (ClutterContainer *container,
- ClutterActor *actor)
+actor_added (ClutterActor *container,
+ ClutterActor *actor)
{
- StScrollView *self = ST_SCROLL_VIEW (container);
- StScrollViewPrivate *priv = self->priv;
-
- if (ST_IS_SCROLLABLE (actor))
- {
- priv->child = actor;
-
- /* chain up to StBin::add() */
- st_scroll_view_parent_iface->add (container, actor);
+ StScrollView *scroll = ST_SCROLL_VIEW (container);
+ StScrollViewPrivate *priv = st_scroll_view_get_instance_private (scroll);
- st_scrollable_set_adjustments (ST_SCROLLABLE (actor),
- priv->hadjustment, priv->vadjustment);
- }
- else
+ if (!ST_IS_SCROLLABLE (actor))
{
g_warning ("Attempting to add an actor of type %s to "
- "a StScrollView, but the actor does "
+ "an StScrollView, but the actor does "
"not implement StScrollable.",
- g_type_name (G_OBJECT_TYPE (actor)));
+ G_OBJECT_TYPE_NAME (actor));
+ return;
}
+
+ if (priv->child)
+ g_warning ("Attempting to add an actor of type %s to "
+ "an StScrollView, but the view already contains a %s. "
+ "Was add_child() used repeatedly?",
+ G_OBJECT_TYPE_NAME (actor),
+ G_OBJECT_TYPE_NAME (priv->child));
+
+ set_child (scroll, actor);
}
static void
-st_scroll_view_remove (ClutterContainer *container,
- ClutterActor *actor)
+actor_removed (ClutterActor *container,
+ ClutterActor *actor)
{
- StScrollView *self = ST_SCROLL_VIEW (container);
- StScrollViewPrivate *priv = self->priv;
+ StScrollView *scroll = ST_SCROLL_VIEW (container);
+ StScrollViewPrivate *priv = st_scroll_view_get_instance_private (scroll);
- if (actor == priv->child)
- {
- g_object_ref (priv->child);
+ if (priv->child == actor)
+ set_child (scroll, NULL);
+}
- /* chain up to StBin::remove() */
- st_scroll_view_parent_iface->remove (container, actor);
+static void
+st_scroll_view_init (StScrollView *self)
+{
+ StScrollViewPrivate *priv = st_scroll_view_get_instance_private (self);
+ ClutterActor *scrollbar;
- st_scrollable_set_adjustments (ST_SCROLLABLE (priv->child),
- NULL, NULL);
+ priv->hscrollbar_policy = ST_POLICY_AUTOMATIC;
+ priv->vscrollbar_policy = ST_POLICY_AUTOMATIC;
- g_object_unref (priv->child);
- priv->child = NULL;
- }
- else
- {
- if (actor == priv->vscroll)
- priv->vscroll = NULL;
- else if (actor == priv->hscroll)
- priv->hscroll = NULL;
- else
- g_assert ("Unknown child removed from StScrollView");
+ priv->hadjustment = g_object_new (ST_TYPE_ADJUSTMENT,
+ "actor", self,
+ NULL);
+ scrollbar = g_object_new (ST_TYPE_SCROLL_BAR,
+ "adjustment", priv->hadjustment,
+ "vertical", FALSE,
+ NULL);
+ g_set_weak_pointer (&priv->hscroll, scrollbar);
- clutter_actor_remove_child (CLUTTER_ACTOR (container), actor);
- }
-}
+ priv->vadjustment = g_object_new (ST_TYPE_ADJUSTMENT,
+ "actor", self,
+ NULL);
+ scrollbar = g_object_new (ST_TYPE_SCROLL_BAR,
+ "adjustment", priv->vadjustment,
+ "vertical", TRUE,
+ NULL);
+ g_set_weak_pointer (&priv->vscroll, scrollbar);
-static void
-clutter_container_iface_init (ClutterContainerIface *iface)
-{
- /* store a pointer to the StBin implementation of
- * ClutterContainer so that we can chain up when
- * overriding the methods
- */
- st_scroll_view_parent_iface = g_type_interface_peek_parent (iface);
+ clutter_actor_add_child (CLUTTER_ACTOR (self), priv->hscroll);
+ clutter_actor_add_child (CLUTTER_ACTOR (self), priv->vscroll);
+
+ /* mouse scroll is enabled by default, so we also need to be reactive */
+ priv->mouse_scroll = TRUE;
+ clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE);
- iface->add = st_scroll_view_add;
- iface->remove = st_scroll_view_remove;
+ /* Connect these *after* we've added our internal actors */
+ g_signal_connect (self, "actor-added", G_CALLBACK (actor_added), NULL);
+ g_signal_connect (self, "actor-removed", G_CALLBACK (actor_removed), NULL);
}
/**
return g_object_new (ST_TYPE_SCROLL_VIEW, NULL);
}
+/**
+ * st_scroll_view_get_child:
+ * @scroll: a #StBin
+ *
+ * Gets the #StScrollable content of @scroll.
+ *
+ * Returns: (transfer none) (nullable): a #StScrollable, or %NULL
+ */
+StScrollable *
+st_scroll_view_get_child (StScrollView *scroll)
+{
+ StScrollViewPrivate *priv;
+
+ g_return_val_if_fail (ST_IS_SCROLL_VIEW (scroll), NULL);
+
+ priv = st_scroll_view_get_instance_private (scroll);
+
+ if (!priv->child)
+ return NULL;
+
+ return ST_SCROLLABLE (priv->child);
+}
+
+/**
+ * st_scroll_view_set_child:
+ * @scroll: a #StScrollView
+ * @child: (nullable): a #StScrollable, or %NULL
+ *
+ * Sets @child as the content of @scroll.
+ *
+ * If @scroll already has a child, the previous child is removed.
+ */
+void
+st_scroll_view_set_child (StScrollView *scroll,
+ StScrollable *child)
+{
+ StScrollViewPrivate *priv;
+
+ g_return_if_fail (ST_IS_SCROLL_VIEW (scroll));
+ g_return_if_fail (child == NULL || ST_IS_SCROLLABLE (child));
+
+ priv = st_scroll_view_get_instance_private (scroll);
+
+ g_object_freeze_notify (G_OBJECT (scroll));
+
+ if (priv->child)
+ clutter_actor_remove_child (CLUTTER_ACTOR (scroll), priv->child);
+
+ if (child)
+ clutter_actor_add_child (CLUTTER_ACTOR (scroll),
+ CLUTTER_ACTOR (child));
+
+ g_object_thaw_notify (G_OBJECT (scroll));
+}
+
/**
* st_scroll_view_get_hscroll_bar:
* @scroll: a #StScrollView
ClutterActor *
st_scroll_view_get_hscroll_bar (StScrollView *scroll)
{
+ StScrollViewPrivate *priv;
+
g_return_val_if_fail (ST_IS_SCROLL_VIEW (scroll), NULL);
- return scroll->priv->hscroll;
+ priv = st_scroll_view_get_instance_private (scroll);
+
+ return priv->hscroll;
}
/**
ClutterActor *
st_scroll_view_get_vscroll_bar (StScrollView *scroll)
{
+ StScrollViewPrivate *priv;
+
g_return_val_if_fail (ST_IS_SCROLL_VIEW (scroll), NULL);
- return scroll->priv->vscroll;
+ priv = st_scroll_view_get_instance_private (scroll);
+
+ return priv->vscroll;
}
/**
gfloat
st_scroll_view_get_column_size (StScrollView *scroll)
{
+ StScrollViewPrivate *priv;
gdouble column_size;
- g_return_val_if_fail (scroll, 0);
+ g_return_val_if_fail (ST_IS_SCROLL_VIEW (scroll), 0);
+
+ priv = st_scroll_view_get_instance_private (scroll);
- g_object_get (scroll->priv->hadjustment,
+ g_object_get (priv->hadjustment,
"step-increment", &column_size,
NULL);
st_scroll_view_set_column_size (StScrollView *scroll,
gfloat column_size)
{
- g_return_if_fail (scroll);
+ StScrollViewPrivate *priv;
+
+ g_return_if_fail (ST_IS_SCROLL_VIEW (scroll));
+
+ priv = st_scroll_view_get_instance_private (scroll);
if (column_size < 0)
{
- scroll->priv->column_size_set = FALSE;
- scroll->priv->column_size = -1;
+ priv->column_size_set = FALSE;
+ priv->column_size = -1;
}
else
{
- scroll->priv->column_size_set = TRUE;
- scroll->priv->column_size = column_size;
+ priv->column_size_set = TRUE;
+ priv->column_size = column_size;
- g_object_set (scroll->priv->hadjustment,
- "step-increment", (gdouble) scroll->priv->column_size,
+ g_object_set (priv->hadjustment,
+ "step-increment", (double) priv->column_size,
NULL);
}
}
gfloat
st_scroll_view_get_row_size (StScrollView *scroll)
{
+ StScrollViewPrivate *priv;
gdouble row_size;
- g_return_val_if_fail (scroll, 0);
+ g_return_val_if_fail (ST_IS_SCROLL_VIEW (scroll), 0);
+
+ priv = st_scroll_view_get_instance_private (scroll);
- g_object_get (scroll->priv->vadjustment,
+ g_object_get (priv->vadjustment,
"step-increment", &row_size,
NULL);
st_scroll_view_set_row_size (StScrollView *scroll,
gfloat row_size)
{
- g_return_if_fail (scroll);
+ StScrollViewPrivate *priv;
+
+ g_return_if_fail (ST_IS_SCROLL_VIEW (scroll));
+
+ priv = st_scroll_view_get_instance_private (scroll);
if (row_size < 0)
{
- scroll->priv->row_size_set = FALSE;
- scroll->priv->row_size = -1;
+ priv->row_size_set = FALSE;
+ priv->row_size = -1;
}
else
{
- scroll->priv->row_size_set = TRUE;
- scroll->priv->row_size = row_size;
+ priv->row_size_set = TRUE;
+ priv->row_size = row_size;
- g_object_set (scroll->priv->vadjustment,
- "step-increment", (gdouble) scroll->priv->row_size,
+ g_object_set (priv->vadjustment,
+ "step-increment", (double) priv->row_size,
NULL);
}
}
g_return_if_fail (ST_IS_SCROLL_VIEW (scroll));
- priv = ST_SCROLL_VIEW (scroll)->priv;
+ priv = st_scroll_view_get_instance_private (scroll);
if (priv->mouse_scroll != enabled)
{
g_return_val_if_fail (ST_IS_SCROLL_VIEW (scroll), FALSE);
- priv = ST_SCROLL_VIEW (scroll)->priv;
+ priv = st_scroll_view_get_instance_private (scroll);
return priv->mouse_scroll;
}
g_return_if_fail (ST_IS_SCROLL_VIEW (scroll));
- priv = ST_SCROLL_VIEW (scroll)->priv;
+ priv = st_scroll_view_get_instance_private (scroll);
if (priv->overlay_scrollbars != enabled)
{
g_return_val_if_fail (ST_IS_SCROLL_VIEW (scroll), FALSE);
- priv = ST_SCROLL_VIEW (scroll)->priv;
+ priv = st_scroll_view_get_instance_private (scroll);
return priv->overlay_scrollbars;
}
g_return_if_fail (ST_IS_SCROLL_VIEW (scroll));
- priv = ST_SCROLL_VIEW (scroll)->priv;
+ priv = st_scroll_view_get_instance_private (scroll);
if (priv->hscrollbar_policy == hscroll && priv->vscrollbar_policy == vscroll)
return;